#include "lv_task.h"
#include <lvgl.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "../task_config.h"
#include "esp_timer.h"

const char* lvgl_task_TAG = "TASK_LVGL";
static SemaphoreHandle_t lvgl_task_mux;

static void increase_lvgl_tick(void *arg)
{
    lv_tick_inc(LVGL_TICK_PERIOD_MS);
}

static void lvgl_task_run(void* arg)
{
    uint32_t task_delay_ms = LVGL_TASK_MAX_DELAY_MS;
    while (1)
    {
        // Lock the mutex due to the LVGL APIs are not thread-safe
        if (task_lvgl_lock(-1))
        {
            task_delay_ms = lv_timer_handler();
            // Release the mutex
            task_lvgl_unlock();
        }
        if (task_delay_ms > LVGL_TASK_MAX_DELAY_MS)
        {
            task_delay_ms = LVGL_TASK_MAX_DELAY_MS;
        }
        else if (task_delay_ms < LVGL_TASK_MIN_DELAY_MS)
        {
            task_delay_ms = LVGL_TASK_MIN_DELAY_MS;
        }
        vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
    }
}

void task_lvgl_init()
{
    ESP_LOGI(lvgl_task_TAG, "Install LVGL tick timer");
    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &increase_lvgl_tick,
        .name = "lvgl_tick"};
    esp_timer_handle_t lvgl_tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD_MS * 1000));

    ESP_LOGI(lvgl_task_TAG, "create LVGL task");
    lvgl_task_mux = xSemaphoreCreateMutex();
    assert(lvgl_task_mux);
    xTaskCreate(lvgl_task_run, "LVGL", LVGL_TASK_STACK_SIZE, NULL, LVGL_TASK_PRIORITY, NULL);
    ESP_LOGI(lvgl_task_TAG, "LVGL task initialized");
}

bool task_lvgl_lock(int timeout_ms)
{
    assert(lvgl_task_mux && "LVGL task must be initialized first");

    const TickType_t timeout_ticks = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
    return xSemaphoreTake(lvgl_task_mux, timeout_ticks) == pdTRUE;
}

void task_lvgl_unlock()
{
    assert(lvgl_task_mux && "LVGL task must be initialized first");
    xSemaphoreGive(lvgl_task_mux);
}

